script 标签的 defer 与 async

##两者共同点
defer 与 async 都是在 HTML 文件中的 script 标签的属性. 两个属性都会通知浏览器立刻下载该脚本, 并且都只对外联
脚本起作用.
添加了这两个属性的 script 脚本不应该包含 DOM 操作, 因为延迟或异步脚本之后,并不能保证脚本的执行顺序,有可能在 DOMContentLoad 事件之前就已经执行了. 但是他们的区别在哪?

##defer 延迟脚本
defer 会在 DOMContentLoad 事件之前执行,虽然规范中表明 defer 的脚本会按顺序,但是实际中同时加了此属性的 script 脚本并不能保证其执行顺序,所以最好只有一个延迟脚本.

###那是不是意味着加了 defer 的脚本可以放在 head 标签那里 ?
从规范上说, 外联脚本加上了 defer 属性之后,会立刻下载,但不会阻塞浏览器加载其他组件,在浏览器的渲染工作完成之后
且在 DomContentLoad 事件之前发生. 也就是说放在 head 中加上 defer 属性的 script 脚本与放在文档底部效果相同.
但是有些浏览器并不支持 defer 属性,因此对于 defer 脚本放在 body 的闭合标签之前仍是最佳选择.实际上, defer 的脚本不一定会按顺序执行.

##async 异步脚本
加上 async 属性的 script 标签会立刻下载脚本,但是不影响浏览器继续渲染页面,但是当脚本下载完成之后,脚本会立刻执
行,这样有可能导致了浏览器的渲染工作停滞,之所以说停滞是因为渲染工作有可能在下载脚本完成之前就已经结束了.
因为 async 的执行时刻不确定性,因此尽量不要将异步脚本放在 head 头部,这样会影响页面加载从而影响用户体验. 换而
言之,它的脚本执行时刻一定会在 load 事件之前,但是可能在 DOMContentLoaded 事件之前或之后,它是为了尽可能快地执行脚本却不想阻碍浏览器的其他工作例如渲染页面.

##区别
下面这张图片很能够说明这两者的关系.